LINEAR_ND_INTERP

Overview

The LINEAR_ND_INTERP function performs piecewise linear interpolation for scattered data in N dimensions (where N > 1). Given a set of known data points with their corresponding values, it estimates values at new, unsampled locations using linear interpolation within a triangulated mesh. This is particularly useful for reconstructing surfaces or multidimensional fields from irregularly spaced measurements.

This implementation uses SciPy’s LinearNDInterpolator class from the scipy.interpolate module. The interpolator constructs a Delaunay triangulation of the input points using Qhull, a computational geometry library. This triangulation partitions the space into simplices (triangles in 2D, tetrahedra in 3D, and higher-dimensional analogues).

For each query point, the algorithm first locates which simplex contains the point. It then computes the interpolated value using barycentric interpolation. Given a point \mathbf{r} inside a simplex with vertices \mathbf{r}_1, \mathbf{r}_2, \ldots, \mathbf{r}_{n+1} and corresponding known values f(\mathbf{r}_1), f(\mathbf{r}_2), \ldots, f(\mathbf{r}_{n+1}), the interpolated value is:

f(\mathbf{r}) \approx \sum_{i=1}^{n+1} \lambda_i \, f(\mathbf{r}_i)

where \lambda_i are the barycentric coordinates of \mathbf{r} with respect to the simplex, satisfying \sum \lambda_i = 1 and 0 \le \lambda_i \le 1 for points inside the simplex.

Points that fall outside the convex hull of the input data cannot be interpolated using this method. The function returns a user-specified fill_value (defaulting to 0) for such points. For alternative approaches that handle extrapolation or use different interpolation schemes, see NearestNDInterpolator for nearest-neighbor interpolation or CloughTocher2DInterpolator for smooth cubic interpolation in 2D.

This example function is provided as-is without any representation of accuracy.

Excel Usage

=LINEAR_ND_INTERP(points, values, xi, fill_value)
  • points (list[list], required): The coordinates of the data points (n_points x n_dims)
  • values (list[list], required): The values at the data points (n_points x 1)
  • xi (list[list], required): The points at which to interpolate (n_new_points x n_dims)
  • fill_value (float, optional, default: 0): Value used for points outside the convex hull

Returns (list[list]): Interpolated values as a 2D list, or error message string.

Examples

Example 1: Demo case 1

Inputs:

points values xi
0 0 0 0.5 0.5
1 0 1
0 1 1
1 1 2

Excel formula:

=LINEAR_ND_INTERP({0,0;1,0;0,1;1,1}, {0;1;1;2}, {0.5,0.5})

Expected output:

Result
1

Example 2: Demo case 2

Inputs:

points values xi fill_value
0 0 1 0.5 0 -999
1 0 2 10 10
0 1 3
1 1 4

Excel formula:

=LINEAR_ND_INTERP({0,0;1,0;0,1;1,1}, {1;2;3;4}, {0.5,0;10,10}, -999)

Expected output:

Result
1.5
-999

Example 3: Demo case 3

Inputs:

points values xi
0 0 0 0 0.25 0.25 0.25
1 0 0 1
0 1 0 2
0 0 1 3

Excel formula:

=LINEAR_ND_INTERP({0,0,0;1,0,0;0,1,0;0,0,1}, {0;1;2;3}, {0.25,0.25,0.25})

Expected output:

Result
1.5

Example 4: Demo case 4

Inputs:

points values xi fill_value
0 0 0 1 0 0
2 0 4 0 1
0 2 4 1 1
2 2 8

Excel formula:

=LINEAR_ND_INTERP({0,0;2,0;0,2;2,2}, {0;4;4;8}, {1,0;0,1;1,1}, 0)

Expected output:

Result
2
2
4

Python Code

import math
import numpy as np
from scipy.interpolate import LinearNDInterpolator as scipy_LinearNDInterpolator

def linear_nd_interp(points, values, xi, fill_value=0):
    """
    Piecewise linear interpolator in N > 1 dimensions.

    See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.LinearNDInterpolator.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        points (list[list]): The coordinates of the data points (n_points x n_dims)
        values (list[list]): The values at the data points (n_points x 1)
        xi (list[list]): The points at which to interpolate (n_new_points x n_dims)
        fill_value (float, optional): Value used for points outside the convex hull Default is 0.

    Returns:
        list[list]: Interpolated values as a 2D list, or error message string.
    """
    def to2d(x):
        return [[x]] if not isinstance(x, list) else x

    # Normalize inputs to 2D lists
    points = to2d(points)
    values = to2d(values)
    xi = to2d(xi)

    # Validate that inputs are 2D lists
    if not isinstance(points, list) or not all(isinstance(row, list) for row in points):
        return "Invalid input: points must be a 2D list."
    if not isinstance(values, list) or not all(isinstance(row, list) for row in values):
        return "Invalid input: values must be a 2D list."
    if not isinstance(xi, list) or not all(isinstance(row, list) for row in xi):
        return "Invalid input: xi must be a 2D list."

    # Validate fill_value
    if not isinstance(fill_value, (int, float)):
        return "Invalid input: fill_value must be a number."
    fill_value = float(fill_value)

    # Check dimensions
    if len(points) == 0:
        return "Invalid input: points must not be empty."
    if len(values) == 0:
        return "Invalid input: values must not be empty."
    if len(xi) == 0:
        return "Invalid input: xi must not be empty."

    if len(points) != len(values):
        return "Invalid input: points and values must have the same number of rows."

    # Validate that all rows have consistent dimensions
    n_dims = len(points[0])
    if n_dims < 2:
        return "Invalid input: points must have at least 2 dimensions (N > 1)."

    for i, row in enumerate(points):
        if len(row) != n_dims:
            return f"Invalid input: all rows in points must have the same length (row {i} mismatch)."

    for i, row in enumerate(values):
        if len(row) != 1:
            return f"Invalid input: values must be a column vector (row {i} has {len(row)} columns)."

    xi_dims = len(xi[0])
    if xi_dims != n_dims:
        return f"Invalid input: xi must have same dimensions as points ({n_dims})."

    for i, row in enumerate(xi):
        if len(row) != xi_dims:
            return f"Invalid input: all rows in xi must have the same length (row {i} mismatch)."

    # Validate that all values are numeric and finite
    try:
        for i, row in enumerate(points):
            for j, val in enumerate(row):
                if not isinstance(val, (int, float)):
                    return f"Invalid input: points[{i}][{j}] must be numeric."
                if math.isinf(val) or math.isnan(val):
                    return f"Invalid input: points[{i}][{j}] must be finite."

        for i, row in enumerate(values):
            val = row[0]
            if not isinstance(val, (int, float)):
                return f"Invalid input: values[{i}][0] must be numeric."
            if math.isinf(val) or math.isnan(val):
                return f"Invalid input: values[{i}][0] must be finite."

        for i, row in enumerate(xi):
            for j, val in enumerate(row):
                if not isinstance(val, (int, float)):
                    return f"Invalid input: xi[{i}][{j}] must be numeric."
                if math.isinf(val) or math.isnan(val):
                    return f"Invalid input: xi[{i}][{j}] must be finite."
    except Exception as e:
        return f"Invalid input: error validating numeric values: {e}"

    # Convert to numpy arrays
    try:
        points_arr = np.array(points, dtype=float)
        values_arr = np.array(values, dtype=float).flatten()
        xi_arr = np.array(xi, dtype=float)
    except Exception as e:
        return f"Invalid input: error converting to arrays: {e}"

    # Perform interpolation
    try:
        interp = scipy_LinearNDInterpolator(points_arr, values_arr, fill_value=fill_value)
        result = interp(xi_arr)
    except Exception as e:
        return f"scipy.interpolate.LinearNDInterpolator error: {e}"

    # Convert result to 2D list
    try:
        result_2d = [[float(val)] for val in result]
    except Exception as e:
        return f"Error converting result to 2D list: {e}"

    return result_2d

Online Calculator